/**
 *
 */
package name.yzhu.index.classify;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import name.yzhu.index.StopWordsHandler;
import name.yzhu.index.stem.IStem;

/**
 * @author yhzhu
 *
 */
public class BayesClassifier {
	static private final Log LOG = LogFactory.getLog(BayesClassifier.class);

	private TrainingDataManager tdm;//训练集管理器
	private static double zoomFactor = 10.0f;
	private IStem stem = null;

	/**
	* 默认的构造器，初始化训练集
	*/
	public BayesClassifier(IStem stem, TrainingDataManager tdm) {
		this.stem = stem;
		this.tdm = tdm;
	}

	/**
	* 计算给定的文本属性向量X在给定的分类Cj中的类条件概率
	* <code>ClassConditionalProbability</code>连乘值
	* @param terms 给定的文本属性向量
	* @param Cj 给定的类别
	* @return 分类条件概率连乘值，即<br>
	*/
	private float calcProd(List<String> terms, String Cj){
		float ret = 1.0F;
		// 类条件概率连乘
		for (String term : terms){
			//因为结果过小，因此在连乘之前放大10倍，这对最终结果并无影响，因为我们只是比较概率大小而已
			ret *=ClassConditionalProbability.calculatePxc(tdm, term, Cj)*zoomFactor;
		}
		// 再乘以先验概率
		ret *= calculatePc(Cj);
		return ret;
	}

	/**
	* 先验概率
	* @param c 给定的分类
	* @return 给定条件下的先验概率
	*/
	private float calculatePc(String c){
		float ret = 0F;
		float Nc = tdm.getTrainingFileCountOfClassification(c);
		float N = tdm.getTrainingFileCount();
		ret = Nc / N;
		return ret;
	}

	/**
	* 去掉停用词
	* @param text 给定的文本
	* @return 去停用词后结果
	*/
	public List<String> DropStopWords(List<String> terms)	{
		List<String> v1 = new ArrayList<String>();
		for(String term : terms)
		{
			if(!StopWordsHandler.getInstance().IsStopWord(term))	{//不是停用词
				v1.add(term);
			}
		}
		return v1;
	}
	/**
	* 对给定的文本进行分类
	* @param text 给定的文本
	* @return 分类结果
	 * @throws IOException
	*/
	public String classify(String text) throws IOException	{
		List<String> terms = stem.stemToList(text);//中文分词处理(分词后结果可能还包含有停用词）
		terms = DropStopWords(terms);//去掉停用词，以免影响分类

		String[] Classes = tdm.getTraningClassifications();//分类
		float probility = 0.0F;
		List<ClassifyResult> crs = new ArrayList<ClassifyResult>();//分类结果
		for (int i = 0; i <Classes.length; i++)	{
			String Ci = Classes[i];//第i个分类
			probility = calcProd(terms, Ci);//计算给定的文本属性向量terms在给定的分类Ci中的分类条件概率
			//保存分类结果
			ClassifyResult cr = new ClassifyResult();
			cr.setClassification(Ci);//分类
			cr.setProbility(probility);//关键字在分类的条件概率
			LOG.debug("In process....");
			LOG.debug(Ci + "：" + probility);
			crs.add(cr);
		}
		//对最后概率结果进行排序
		Collections.sort(crs);
		//返回概率最大的分类
		return crs.get(0).getClassification();
	}

	static class ClassifyResult implements Comparable<ClassifyResult>{
		private double probility;//分类的概率
		private String classification;//分类

		public ClassifyResult()	{
			this.setProbility(0);
			this.setClassification(null);
		}

		public void setProbility(double probility) {
			this.probility = probility;
		}

		public double getProbility() {
			return probility;
		}

		public void setClassification(String classification) {
			this.classification = classification;
		}

		public String getClassification() {
			return classification;
		}

		@Override
		public int compareTo(ClassifyResult o) {
			final double ret = getProbility() - o.getProbility();
			if (ret < 0)
				return 1;
			else
				return -1;
		}
	}
}
